#include "my.h"
#include "transform.h"
#include <cmath>

const float width = 800.0f;
const float height = 600.0f;
glm::mat4 projMat = glm::perspective(
    glm::radians(45.0f), // FoV: 视角
    width / height, // aspect ratio: 宽高比
    0.1f, 100.0f // 近点和远点
);

// triangle
struct Scene00 : public Scene {
    static float vertices[];
    static int sizeofVertices; // 为了通过编译, 加此字段

    void init() {
        initVBO(vertices, sizeofVertices);
        initVAO(1, 3);
        shader.init("shader/00.vertex.glsl", "shader/00.frag.glsl");
    }

    void render() {
        Scene::render();
        glDrawArrays(GL_TRIANGLES, 0, 6);
    }
} scene00;

float Scene00::vertices[] = {
    -0.5f, -0.5f, 0.0f,
     0.5f, -0.5f, 0.0f,
     0.0f,  0.5f, 0.0f,

     0.6, -0.5, 0,
     0.8, -0.5, 0,
     0.7, 0.5, 0
};
int Scene00::sizeofVertices = sizeof(vertices);

// rectangle wireframe
struct Scene01 : public Scene {

    static float vertices[];
    static int sizeofVertices;

    static unsigned int indices[];
    static int sizeofIndices;

    void init() {
        initVBO(vertices, sizeofVertices);
        initVAO(1, 3);
        initEBO(indices, sizeofIndices);

        // configure gl wireframe mode
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // default: GL_FILL

        shader.init("shader/01.vertex.glsl", "shader/01.frag.glsl");
    }

    void render() {
        Scene::render();

        float timeValue = glfwGetTime();
        float green = (sin(2*timeValue) / 2.0f) + 0.5f;
        shader.set("ourColor", 0, green, 0, 1);

        //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); // EBO is auto binded
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    }

    void destroy() {
        glDeleteBuffers(1, &EBO);
        Scene::destroy();
    }
} scene01;

float Scene01::vertices[] = {
    .5,  .5, 0,
    .5, -.5, 0,
   -.5, -.5, 0,
   -.5,  .5, 0
};
int Scene01::sizeofVertices = sizeof(vertices);
unsigned int Scene01::indices[] = {
    0, 1, 3, // first triangle
    1, 2, 3  // second triangle
};
int Scene01::sizeofIndices = sizeof(indices);

// rgb triangle
struct Scene02 : public Scene {

    static float vertices[];
    static int sizeofVertices;

    void init() {
        initVBO(vertices, sizeofVertices);
        initVAO(2, 3, 3);
        shader.init("shader/02.vertex.glsl", "shader/02.frag.glsl");
    }

    void render() {
        Scene::render();
        glDrawArrays(GL_TRIANGLES, 0, 3);
    }
} scene02;

float Scene02::vertices[] = {
    // positions         // colors
     0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // bottom right
    -0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // bottom left
     0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // top
};
int Scene02::sizeofVertices = sizeof(vertices);

// container texture
struct Scene03 : public Scene {

    static float vertices[];
    static int sizeofVertices;

    static unsigned int indices[];
    static int sizeofIndices;

    void initCommons() {
        initVBO(vertices, sizeofVertices);
        initVAO(3, 3, 3, 2);
        initEBO(indices, sizeofIndices);
        initTexture(GL_TEXTURE0, "img/container.jpg");
        initTexture(GL_TEXTURE1, "img/awesomeface.png");
    }

    void renderCommons() {
        Scene::render();
        shader.set("tex0", 0).set("tex1", 1);
    }

    virtual void init() {
        initCommons();
        shader.init("shader/03.vertex.glsl", "shader/03.frag.glsl");
    }

    virtual void render() {
        renderCommons();
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    }

    void destroy() {
        glDeleteBuffers(1, &EBO);
        Scene::destroy();
    }
} scene03;

float Scene03::vertices[] = {
    // positions          // colors           // texture coords
     0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // top right
     0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // bottom right
    -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // bottom left
    -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // top left
};
int Scene03::sizeofVertices = sizeof(vertices);
unsigned int Scene03::indices[] = {
    0, 1, 3, // first triangle
    1, 2, 3  // second triangle
};
int Scene03::sizeofIndices = sizeof(indices);

// transform
struct Scene04 : public Scene03 {

    void init() {
        initCommons();
        shader.init("shader/04.vertex.glsl", "shader/04.frag.glsl");
    }

    void render() {
        renderCommons();

        // scale 0.5,
        // then rotate time*16
        // then translate (0.5 -0.5)
        glm::mat4 trans = Transform()
            .translate(glm::vec3(0.5, -0.5, 0))
            .rotate(glfwGetTime()*16)
            .scale(0.5).get();
        shader.set("transform", trans);

        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    }

} scene04;

// 3d scene
struct Scene05 : public Scene {

    static float vertices[];
    static int sizeofVertices;
    static glm::vec3 cubePositions[];
    static int cubeCount;

    void init() {
        initVBO(vertices, sizeofVertices);
        initVAO(2, 3, 2);
        initTexture(GL_TEXTURE0, "img/container.jpg");
        initTexture(GL_TEXTURE1, "img/awesomeface.png");

        glEnable(GL_DEPTH_TEST); // z buffer 消隐

        shader.init("shader/05.vertex.glsl", "shader/05.frag.glsl");
    }

    void render() {
        Scene::render();
        shader.set("tex0", 0).set("tex1", 1);

        glm::mat4 viewMat = Transform()
            .translate(glm::vec3(0, 0, -3)).get();
        shader.set("view", viewMat)  // 相机位置
              .set("proj", projMat); // 透视投影

        for (int i = 0; i < cubeCount; ++i) {
            glm::mat4 modelMat = Transform()
                .translate(cubePositions[i])
                .rotate(
                    i % 3 == 0 ? glfwGetTime() * 10 * (5-i) : 20 * i,
                    glm::vec3(1, 0.3, 0.5)
                ).get();
            shader.set("model", modelMat); // 世界坐标
            // 每个面 6 个顶点, 共 6 个面
            glDrawArrays(GL_TRIANGLES, 0, 36);
        }

    }

} scene05;

float Scene05::vertices[] = {
    // vertex             // texture
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
     0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
     0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
     0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
     0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
     0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
     0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
     0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
    -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
    -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
};
int Scene05::sizeofVertices = sizeof(vertices);
glm::vec3 Scene05::cubePositions[] = {
    glm::vec3( 0.0f,  0.0f,  0.0f), 
    glm::vec3( 2.0f,  5.0f, -15.0f), 
    glm::vec3(-1.5f, -2.2f, -2.5f),  
    glm::vec3(-3.8f, -2.0f, -12.3f),  
    glm::vec3( 2.4f, -0.4f, -3.5f),  
    glm::vec3(-1.7f,  3.0f, -7.5f),  
    glm::vec3( 1.3f, -2.0f, -2.5f),  
    glm::vec3( 1.5f,  2.0f, -2.5f), 
    glm::vec3( 1.5f,  0.2f, -1.5f), 
    glm::vec3(-1.3f,  1.0f, -1.5f)  
};
int Scene05::cubeCount = sizeof(cubePositions) / sizeof(glm::vec3);

/* @implement */
Scene *scenes[] = {
    &scene00,
    &scene01,
    &scene02,
    &scene03,
    &scene04,
    &scene05
};

/* @implement */
int nScenes = sizeof(scenes) / sizeof(Scene*);
